

%---------- MÁQUINA VIRTUAL PARA EL TRADUCTOR ---------------


%----Procesando el archivo de código objeto

listaCaract(end_of_file, _, []) :- !.
listaCaract(Char,In,[Char|Xs]):-
        get_char(In, Char1),
    listaCaract(Char1,In, Xs).

procesa(File, Xs) :-
    open(File, read, In),
    get_char(In, Char),
    listaCaract(Char, In, Xs),
    close(In).


%Predicado para recuperar instrucciones del programa

instr([I|_],0,I):-!.
instr([_|Is],N,I):-
	NN is N-1,
	instr(Is,NN,I).

%Predicados para leer y escribir datos de la memoria de la máquina

leer([(Dir,Val)|_],Dir,Val):-!.
leer([_|M],Dir,Val):-leer(M,Dir,Val).
leer([],_,0).

escribir([(Dir,_)|Ms],Dir,Val,[(Dir,Val)|Ms]):-!.
escribir([M|Ms],Dir,Val,[M|NMs]):-escribir(Ms,Dir,Val,NMs).
escribir([],Dir,Val,[(Dir,Val)]).


%Representación del estado de la máquina virtual
%
%     estado(programa,pila,memoria,cp)

estadoInicial(Programa,estado(Programa,[],[],0)).

%Predicados para recuperar cada uno de los elementos del estado de la máquina

programa(estado(Programa,_,_,_),Programa).
pila(estado(_,Pila,_,_),Pila).
memoria(estado(_,_,Memoria,_),Memoria).
cp(estado(_,_,_,CP),CP).

%Predicados para actualizar los elementos del estado : pila,memoria,cp.

ponPila(estado(Programa,_,Memoria,CP),Pila,estado(Programa,Pila,Memoria,CP)).
ponMemoria(estado(Programa,Pila,_,CP),Memoria,estado(Programa,Pila,Memoria,CP)).
ponCP(estado(Programa,Pila,Memoria,_),CP,estado(Programa,Pila,Memoria,CP)).

%Predicado para consumir la cabecera de la pila

%Si intenta consumir la cabecera de pila vacía devuelve falso.
consumePila([N|Ps],Ps,N).

%Implementación de las instrucciones de la máquina

%------------------- INSTRUCCIONES DE MANIPULACIÓN DE LA PILA DE EVALUACIÓN Y DE LA MEMORIA -----------------------

%apila_i(valor)

ejecutaInstr(apila_i(N),E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	ponPila(E0,[N|Pila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%apila_r(número_real)

ejecutaInstr(apila_r(N),E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	ponPila(E0,[N|Pila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).


%apila_dir(dirección) 

ejecutaInstr(apila_dir(Dir),E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	%memoria(E0,Memoria),
	%leer(Memoria,Dir,Val),
	ponPila(E0,[Dir|Pila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%apila_ind

ejecutaInstr(apila_ind,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	memoria(E,Memoria),
	consumePila(Pila,NPila,Val),
	leer(Memoria,Val,Val2),
	ponPila(E,[Val2|NPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%apila_valor

%ejecutaInstr(apila_valor,E,EN):-
%	pila(E,Pila),
%	cp(E,CP),
%	memoria(E,Memoria),

%desapila_dir(dirección)

ejecutaInstr(desapila_dir(Dir),E0,E3):-
	pila(E0,Pila),
	cp(E0,CP),
	memoria(E0,Memoria),
        consumePila(Pila,NPila,Val),
	ponPila(E0,NPila,E1),
	escribir(Memoria,Dir,Val,NMemoria),
	NCP is CP+1,
	ponMemoria(E1,NMemoria,E2),
	ponCP(E2,NCP,E3).

%desapila_ind

ejecutaInstr(desapila_ind,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	memoria(E,Memoria),
	consumePila(Pila,NPila,Val),
	consumePila(NPila,NNPila,Dir),
	escribir(Memoria,Dir,Val,NMemoria),
	NCP is CP+1,
	ponPila(E,NNPila,E1),
	ponMemoria(E1,NMemoria,E2),
	ponCP(E2,NCP,EN).

%mueve

/*ejecutaInstr(mueve(0),E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,_),
	consumePila(NPila,NNPila,_),
	ponPila(E,NNPila,E1),
	NCP is CP + 1,
	ponCP(E1,NCP,EN).
	

ejecutaInstr(mueve(Cant),E,EN):-
	NCant is Cant-1,
	pila(E,Pila),
	memoria(E,Memoria),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,_NNPila,Val2),
	Dir1 is Val1 + NCant,
	Dir2 is Val2 + NCant,
	leer(Memoria,Dir1,Val),
	escribir(Memoria,Dir2,Val,NMemoria),
	ponMemoria(E,NMemoria,E1),
	ejecutaInstr(mueve(NCant),E1,EN).*/
ejecutaInstr(mueve(Cant),C,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	memoria(E,Memoria),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	Dir1 is Val1 + C,
	Dir2 is Val2 + C,
	leer(Memoria,Dir1,Val),
	escribir(Memoria,Dir2,Val,NMemoria),
	ponMemoria(E,NMemoria,E1),
	NC is C+1,
	(NC < Cant ->
		(ejecutaInstr(mueve(Cant),NC,E1,EN))
		;
		(ponPila(E1,NNPila,E2),
		 NCP is CP + 1,
		 ponCP(E2,NCP,EN)
		)
	).

ejecutaInstr(mueve(Cant),E,EN):-
	ejecutaInstr(mueve(Cant),0,E,EN).
	
%copia_cima  

/*ejecutaInstr(copia_cima,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	memoria(E,Memoria),
	%En Val la cima
	consumePila(Pila,NPila,Val),
	%En Val2 la subcima
	consumePila(NPila,NNPila,Val2),
	escribirMemoria(Memoria,Val2,Val,NMemoria),
	ponPila(E,[Val|NNPila],E1),%revisar a ver si esto es realmente así
	NCP is CP+1,
	ponMemoria(E1,NMemoria,E2),
	ponCP(E2,NCP,EN).*/

%copia_cima <---- APAÑO TENEBROSO!!!!! HACEMOS QUE DE MOMENTO SE COMPORTE COMO APILA_IND
ejecutaInstr(copia_cima,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	memoria(E,Memoria),
	consumePila(Pila,NPila,Val),
	leer(Memoria,Val,Val2),
	ponPila(E,[Val2|NPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%elimina_cima

/*ejecutaInstr(elimina_cima,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,_),
	ponPila(E,NPila,E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).*/

%elimina_cima <------------ OTRO APAÑO OSCURO!!! DE MOMENTO HACEMOS QUE NO HAGA NADA
ejecutaInstr(elimina_cima,E,E).

%------------------- INSTRUCCIONES ARITMÉTICO - LÓGICAS ------------------------------------

%suma

ejecutaInstr(suma,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 + Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%resta

ejecutaInstr(menos,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 - Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%multiplica

ejecutaInstr(mult,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 * Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%divide

ejecutaInstr(divide,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 / Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%módulo

ejecutaInstr(modulo,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 mod Val1,
	ponPila(E,[N|NNPila],E1),
	NCP is CP + 1,
	ponCP(E1,NCP,EN).


%----------Para las operaciones lógicas, se considera 0 -> falso y 1 -> cierto

%mayor

ejecutaInstr(mayor,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	(Op2 > Op1 ->
		(Res is 1)
		;
		(Res is 0)
	),
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%mayor o igual

ejecutaInstr(mayIg,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	(Op2 >= Op1 ->
		(Res is 1)
		;
		(Res is 0)
	),
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%menor

ejecutaInstr(menor,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	(Op2 < Op1 ->
		(Res is 1)
		;
		(Res is 0)
	),
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%menor o igual
ejecutaInstr(menIg,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	(Op2 =< Op1 ->
		(Res is 1)
		;
		(Res is 0)
	),
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

igual(X,X).

%igual
ecutaInstr(igual,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	(igual(Op2,Op1) -> 
		(Res is 1)
		;
		(Res is 0)
	),
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

distinto(X,Y):- \+igual(X,Y).

%distinto
ejecutaInstr(igual,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	(distinto(Op2,Op1) -> 
		(Res is 1)
		;
		(Res is 0)
	),
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%and
ejecutaInstr(opy,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	Res is Op2 /\ Op1,
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%or
ejecutaInstr(opo,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	consumePila(NPila,NNPila,Op2),
	Res is Op2 \/ Op1,
	ponPila(E,[Res|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).

%not
ejecutaInstr(neg,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Op1),
	(igual(Op1,0) ->
		 (Res is 1)
		 ;
		 (Res is 0)
	),
	ponPila(E,[Res|NPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,EN).
	
	

%---------------------- INSTRUCIONES DE CONTROL DE SECUENCIA (SALTOS) ------------------------------------

	

%ir_a(dir)

ejecutaInstr(ir_a(Dir),E,EN):-
	%cp(E,CP),<--Esto en principio aquí sobraría
	NCP is Dir,
	ponCP(E,NCP,EN).

%ir_f(dir)

ejecutaInstr(ir_f(Dir),E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Cond),
	(Cond==0 ->
		(NCP is Dir);
		(NCP is CP+1)
	),
	ponPila(E,NPila,E1),
	ponCP(E1,NCP,EN).

%ir_v(dir)

ejecutaInstr(ir_v(Dir),E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,NPila,Cond),
	Cond\=0,%(ó == 1)
	NCP is Dir,
	!,
	NCP is CP+1,
	ponPila(E,NPila,E1),
	ponCP(E1,NCP,EN).

%ir_ind

ejecutaInstr(ir_ind,E,EN):-
	pila(E,Pila),
	consumePila(Pila,NPila,Val),
	NCP is Val,
	ponPila(E,NPila,E1),
	ponCP(E1,NCP,EN).

%----------------------- INSTRUCCIONES DE ENTRADA/SALIDA ------------------------------------------------

%escribir
ejecutaInstr(escribir,E,EN):-
	pila(E,Pila),
	cp(E,CP),
	consumePila(Pila,_NPila,Val),
	NCP is CP+1,
	write(Val),nl,
	ponCP(E,NCP,EN).



%----------------------- IMPLEMENTACIÓN DEL CICLO DE EJECUCIÓN ------------------------------------------

ejecuta(Estado,Final):-
	programa(Estado,Programa),
	cp(Estado,CP),
	instr(Programa,CP,Instr),
	Instr\=stop,
	!,
	ejecutaInstr(Instr,Estado,NEstado),
	ejecuta(NEstado,Final).

ejecuta(Estado,Estado).



%----------------------- PREDICADOS DE PRUEBA PARA TRATAR EL ARCHIVO OBJETO ----------------------

%concatena va metiendo los elementos en una lista que va a ser el elemento Programa de la tupla que representa la máquina virtual.

concatena(_Desc,end_of_file,[]).

concatena(Desc,Instr,[Instr|RInstr]):-
	Instr \== end_of_file,
	read(Desc,NInstr),
	concatena(Desc,NInstr,RInstr).


procesarFichero(Fichero,Lista):-
	open(Fichero, read, Desc),
	read(Desc,Instr),
	concatena(Desc,Instr,Lista),
	close(Desc).

%--------------------- PREDICADO PARA LANZAR LA MÁQUINA VIRTUAL -----------------------------------

run(Fichero,Resultado):-
	procesarFichero(Fichero,Programa),
	estadoInicial(Programa,Estado),
	ejecuta(Estado,Resultado).
